package com.oop.example.work.esport;

import antlr.output.MultiFarLexer;
import com.oop.example.processing.ErrorException;
import com.oop.example.processing.ParseError;
import com.oop.example.processing.Parser;
import com.oop.example.processing.TockenMap;
import com.oop.example.processing.WarnException;
import com.oop.example.processing.msdoc.HtmlPreparer;
import com.oop.example.utils.Utils;
import com.oop.example.xml.Block;
import com.oop.example.xml.Custom;
import com.oop.example.xml.Entry;
import com.oop.example.xml.Entry.Content;
import com.oop.example.xml.Example;
import com.oop.example.xml.Homonym;
import com.oop.example.xml.MarkType;
import com.oop.example.xml.Near;
import com.oop.example.xml.Phrase;
import com.oop.example.xml.Phrase.Label;
import com.oop.example.xml.PhraseWithAttribs;
import com.oop.example.xml.Reference;
import com.oop.example.xml.Sense;
import com.oop.example.xml.SubSense;
import com.oop.example.xml.Transcription;
import com.oop.example.xml.Translation;
import com.oop.example.xml.Translation.Far;
import com.oop.example.xml.Variant;
import java.io.StringReader;
import java.util.Properties;
import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBElement;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.Token;

/**
 *
 * @author -
 */
public class Esp2PortHtmlParser extends Parser {

    boolean warn = false;
    private LabelInserter labelInserter = null;
    private LabelChecker lc = new LabelChecker();

    public Esp2PortHtmlParser() {
        Properties config = Utils.getConfig();
        try {
            warn = Boolean.valueOf(config.getProperty(Esp2PortConfig.WARN_EXCEPTION));
        } catch (Exception ex) {
            warn = false;
            ex.printStackTrace(System.out);
        }
    }
    private TockenMap tockenMap = new Esp2PortuTockenMap();

    private boolean checkTokens(Token token) {
        for (int tocketId : tockenMap.getFullTockenMap().keySet()) {
            if (tocketId == token.getType()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean isApplicable() {
        labelInserter = new LabelInserter(factory, tockenMap, this);
        errMessage = "";
        errPos = -1;
        int headerCount = 0;
        boolean hasSubSenseNumeration = false;
        boolean hasColon = false;
        lc.check(this.string);
        try {
            MultiFarLexer lexer = new MultiFarLexer(new ANTLRStringStream(this.string));
            Token nextToken = lexer.nextToken();
            while (nextToken.getType() != Token.EOF) {
                if (nextToken.getType() == MultiFarLexer.COLON) {
                    hasColon = true;
                }
                if (nextToken.getType() == MultiFarLexer.SUB_NUMERATION) {
                    hasSubSenseNumeration = true;
                }
                if (nextToken.getType() == MultiFarLexer.HEADER) {
                    headerCount++;
                }
                if (nextToken.getType() == MultiFarLexer.NUMERATION) {
                    labelInserter.markHasSenseNum();
                }
                if (nextToken.getType() == MultiFarLexer.SUB_NUMERATION) {
                    labelInserter.markHasSubSenseNum();
                }
                if (!checkTokens(nextToken)) {
                    errMessage = "no applicable token " + nextToken.getText() + " pos=" + lexer.getCharPositionInLine();
                    errPos = lexer.getCharPositionInLine();
                    return false;
                }
                nextToken = lexer.nextToken();
            }
            if (!hasColon && hasSubSenseNumeration) {
                errMessage = "Нет токена " + tockenMap.getFullTockenMap().get(MultiFarLexer.COLON);
                errPos = -1;
                return false;
            }
            if (hasColon && !hasSubSenseNumeration) {
                errMessage = "Нет токена " + tockenMap.getFullTockenMap().get(MultiFarLexer.SUB_NUMERATION);
                errPos = -1;
                return false;
            }
        } catch (Exception ex) {
            if (ex instanceof ParseError) {
                errMessage = "error on create lexer " + ex.getMessage();
                errPos = ((ParseError) ex).getPosition();
            }
            ex.printStackTrace(System.out);
            return false;
        }
        return true;
    }

    @Override
    public void parse() throws ParseError {
        Token token = null;

        try {
            MultiFarLexer lexer = new MultiFarLexer(new ANTLRStringStream(this.string));
            token = lexer.nextToken();
            String word = "";
            boolean beginNumeration = false;
            boolean beginSubNumeration = false;
            boolean hasColon = false;
            boolean vLast = false;
            //boolean hasHomonym = false;
            //boolean hasDot = true;
            while (token.getType() != Token.EOF) {
                tockenMap.addTocken(token.getType());
                labelInserter.markBeforeInsert(token.getType());
                if (checkTokens(token)) {
                    if (token.getType() == MultiFarLexer.HEADER) {
                        insertHeader(token.getText());
                        if (headingCount > 1) {
                            tockenMap.addTocken(Esp2PortuTockenMap.DOUBLE_HEADING);
                        }
                    } else if (token.getType() == MultiFarLexer.HOMONYM) {
                        nextHomonym();
                    } else if (token.getType() == MultiFarLexer.ALTERNAT_HEADER) {
                        insertAlterantiveHeader(token.getText());
                    } else if (token.getType() == MultiFarLexer.TRANSCRIPTION) {
                        insertTranscription(token.getText());
                    } else if (token.getType() == MultiFarLexer.LABEL) {
                        vLast = token.getText().equals("<i>{V}</i>");
                        if (beginNumeration || beginSubNumeration) {
                            labelInserter.insertNearLabel(token.getText());
                        } else {
                            labelInserter.insertLabel(token.getText());
                        }

                    } else if (token.getType() == MultiFarLexer.SHARP
                            || token.getType() == MultiFarLexer.DOUBLESHARP
                            || token.getType() == MultiFarLexer.PLUS) {
                        String txt = "+";
                        if (token.getType() == MultiFarLexer.SHARP) {
                            txt = " ";
                        } else if (token.getType() == MultiFarLexer.DOUBLESHARP) {
                            txt = ", ";
                        }
                        if (beginNumeration || beginSubNumeration) {
                            labelInserter.insertNearText(txt);
                        } else {
                            labelInserter.insertText(txt);
                        }
                    } else if (token.getType() == MultiFarLexer.PORTU_WORD) {
                        if (word.trim().isEmpty()) {
                            word = "";
                        }
                        word += token.getText();
                    } else if (token.getType() == MultiFarLexer.COMMA) {
                        near = null;
                    } else if (token.getType() == MultiFarLexer.WS) {
                        word += token.getText();
                    } else if (token.getType() == MultiFarLexer.TERE) {
                        word += token.getText();
                    } else if (token.getType() == MultiFarLexer.NUMERATION) {
                        beginNumeration = true;
                        subSenseCounter = 1;
                        nextSense(token.getText());
                        //handleBeginSense(token.getText());
                    } else if (token.getType() == MultiFarLexer.REFERENCE) {
                        near = null;
                        insertReferece(token.getText());
                    } else if (token.getType() == MultiFarLexer.DOT) {
                        variantClassifier = null;
                        if (beginNumeration && beginSubNumeration) {
                            beginSubNumeration = false;
                        }
                        if (beginNumeration && !beginSubNumeration) {
                            beginNumeration = false;
                        }
                    } else if (token.getType() == MultiFarLexer.EXAMPLE) {
                        insertExample(token.getText());
                    } else if (token.getType() == MultiFarLexer.STAR) {
                        nextVariant();
                        beginNumeration = false;
                        beginSubNumeration = false;
                    } else if (token.getType() == MultiFarLexer.FIXED_EXAMPLE) {
                        insertFixedExample(token.getText(), token.getCharPositionInLine());
                    } else if (token.getType() == MultiFarLexer.VEJA) {
                        //игнорируем
                    } else if (token.getType() == MultiFarLexer.COMMENT) {
                        insertComment(token.getText());
                    } else if (token.getType() == MultiFarLexer.SUB_NUMERATION) {
                        //hasSubNumeration = true;
                        if (hasColon) {
                            beginSubNumeration = true;
                            nextSubSense(token.getText());
                        } else {
                            throw new ParseError(token.getCharPositionInLine(), "Нумерация без символа \":\"");
                        }
                    } else if (token.getType() == MultiFarLexer.COLON) {
                        hasColon = true;
                        subSenseCounter = 1;
                        moveTranslation();
                    } else if (token.getType() == MultiFarLexer.SIN) {
                        insertSynonym(token.getText());
                    } else if (token.getType() == MultiFarLexer.VAR) {
                        insertVar(token.getText());
                    } else if (token.getType() == MultiFarLexer.PART_IRREG) {
                        insertPartIrreg(token.getText());
                    } else if (token.getType() == MultiFarLexer.CUSTOM) {
                        insertCustom(token.getText());
                    } else if (token.getType() == MultiFarLexer.SEMI) {
                        nextFat();
                    } else if (token.getType() == MultiFarLexer.REF_CLAS) {
                        insertRefClas(token.getText());
                    } else if (token.getType() == MultiFarLexer.NUM_FIXED_EXAMPLE) {
                        insertNumFixedExample(token.getText());
                    } else if (token.getType() == MultiFarLexer.TOCLASSIF) {
                        insertWordForm(token.getText());
                    } else if (token.getType() == MultiFarLexer.COMMENT_IN_SIMPLE) {
                        insertEnglish(token.getText());
                    } else if (token.getType() == MultiFarLexer.SIMPLE) {
                        String str = token.getText().replaceAll("\\~", "");
                        insertWord(str);
                    }
                } else {
                    throw new ParseError(-1, "Неизвестный токен");
                }
                labelInserter.markAfterInsert(token.getType());
                token = lexer.nextToken();
                if (token.getType() != MultiFarLexer.PORTU_WORD
                        && token.getType() != MultiFarLexer.WS
                        && token.getType() != MultiFarLexer.TERE
                        && !word.trim().isEmpty()) {
                    if (token.getType() == MultiFarLexer.COLON) {
                        word = word + ":";
                    }
                    if (vLast) {
                        tockenMap.addTocken(Esp2PortuTockenMap.V_LABEL);

                        insertVLabelReferece(word);

                        //перемещение классификатора c пометой V
                        //JAXBElement<?> remove = getSubSense().getSubSenseChildren().remove(0);
                        //getSubSense().getSubSenseChildren().add(1, remove);

                    } else {
                        insertWord(word);
                    }
                    word = "";
                }
            }
            labelInserter.postInsert();
        } catch (Exception ex) {
            int pos = -1;
            if (token != null) {
                pos = token.getCharPositionInLine();
            }
            ex.printStackTrace(System.out);
            if (ex instanceof WarnException) {
                throw (WarnException) ex;
            }
            if (ex instanceof ErrorException) {
                throw (ErrorException) ex;
            }
            if (ex instanceof ParseError) {
                throw (ParseError) ex;
            }
            throw new ParseError(pos, "Неизвестная ошибка [" + ex.getClass().getSimpleName() + "] " + ex.getMessage());
        }
    }

    private void insertWordForm(String txt) {
        String[] split = txt.split("</b>");
        if (split.length != 2) {
            throw new ParseError(-1, "Неверная форма слова");
        }
        PhraseWithAttribs wordClass = factory.createPhraseWithAttribs();
        String tmp = split[0].replace("<CL/><b>", "");
        Block block = factory.createBlock();
        block.setValue(tmp);
        JAXBElement<Block> simple = factory.createNearSimple(block);
        wordClass.getPhraseChildren().add(simple);
        MultiFarLexer localLexer = new MultiFarLexer(new ANTLRStringStream(split[1]));
        Token token = localLexer.nextToken();
        while (token.getType() != Token.EOF) {
            if (token.getType() == MultiFarLexer.LABEL) {
                String label = token.getText().replace("}</i>", "").replace("<i>{", "");
                Label lab = factory.createPhraseLabel();
                lab.setValue(label);
                JAXBElement<Label> jbeLabel = factory.createPhraseLabel(lab);
                wordClass.getPhraseChildren().add(jbeLabel);
            } else if (token.getType() == MultiFarLexer.WS
                    || token.getType() == MultiFarLexer.SHARP) {
                block = factory.createBlock();
                block.setValue(" ");
                simple = factory.createNearSimple(block);
                wordClass.getPhraseChildren().add(simple);
            }
            token = localLexer.nextToken();
        }
        getSubSense().getSubSenseChildren().add(factory.createSubSenseClassifier(wordClass));
    }

    private void insertRefClas(String txt) {
        String tmp = txt.replace("@", "").replace("→", "→ ");
        PhraseWithAttribs refClass = factory.createPhraseWithAttribs();
        Block block = factory.createBlock();
        block.setValue(tmp);

        JAXBElement<Block> simple = factory.createNearSimple(block);
        refClass.getPhraseChildren().add(simple);
        getSubSense().getSubSenseChildren().add(factory.createSubSenseClassifier(refClass));
    }

    public static void main(String[] args) {
        String someTxt = "<h1>Em português, <b>ajo</b> corresponde à primeira pessoa do singular do presente de indicativo do verbo agir, que, em espanhol, é \"actúo\": <i>siempre actúo rápidamente en caso de necesidad</i> / sempre ajo prontamente em caso de necessidade.</h1>";
        String chars = "[a-zA-zÀàÁáÂâÃãÇçÈèÉéÊêÌìÍíÏïÒòÓóÕõÙùÚúÜüÔô]";
        String[] split = someTxt.split("<b>.*</b>");
        for (String str : split) {
            System.out.println(str);
        }
        while (someTxt.contains("<b>") && someTxt.contains("</b>")) {
            int index1 = someTxt.indexOf("<b>");
            int index2 = someTxt.indexOf("</b>") + "</b>".length();

            String pre = someTxt.substring(index1 - 1, index1);
            String post = someTxt.substring(index2, index2 + 1);

            someTxt = someTxt.replaceFirst("<b>", "").replaceFirst("</b>", "");

            System.out.print("pre: '" + pre + "'");
            System.out.println("; post: '" + post + "'");

            if (pre.matches(chars) || post.matches(chars)) {
                throw new ErrorException("жиненькый в пользовательской зоне");
            }
        }

    }

    private void insertCustom(String customTxt) {
        String someTxt = customTxt.replace("<h1>", "").replace("</h1>", "").replaceAll(" </b>", "</b> ");
        String chars = "[a-zA-zÀàÁáÂâÃãÇçÈèÉéÊêÌìÍíÏïÒòÓóÕõÙùÚúÜüÔô]";
        String[] split = someTxt.split("<b>.*</b>");
        for (String str : split) {
            System.out.println(str);
        }
        while (someTxt.contains("<b>") && someTxt.contains("</b>")) {
            int index1 = someTxt.indexOf("<b>");
            int index2 = someTxt.indexOf("</b>") + "</b>".length();


            String pre = "";
            if (index1 <= 0) {
                pre = " ";
            } else {
                pre = someTxt.substring(index1 - 1, index1);
            }
            String post = someTxt.substring(index2, index2 + 1);

            someTxt = someTxt.replaceFirst("<b>", "").replaceFirst("</b>", "");

            //System.out.print("pre: '" + pre + "'");
            //System.out.println("; post: '" + post + "'");

            if (pre.matches(chars) || post.matches(chars)) {
                throw new ErrorException("Жирненькый в пользовательской зоне");
            }
        }
        String tmp = customTxt.replace("<h1>", "").replace("</h1>", "");
        Custom custom = factory.createCustom();
        Block italicBold = TextUtils.getItalicBold(tmp);
        JAXBElement<Block> simple = factory.createNearSimple(italicBold);
        custom.getPhraseChildren().add(simple);
        getSubSense().getSubSenseChildren().add(factory.createSubSenseCustom(custom));
    }

    /**
     * <classifier> <label>Var</label> <simple>: </simple> <reference
     * displayName="translación" referencedLexeme="translación" /> <external />
     * </reference> </classifier>
     *
     * @param varTxt
     */
    private void insertVar(String varTxt) {
        varTxt = varTxt.replaceAll("<i>", "");
        varTxt = varTxt.replaceAll("\\.</i>", "");
        varTxt = varTxt.replaceAll("Var\\:", "");
        varTxt = varTxt.trim();
        //insertSynonym(varTxt);

        PhraseWithAttribs synp = factory.createPhraseWithAttribs();
        Reference.External external = factory.createReferenceExternal();
        Reference ref = factory.createReference();
        ref.setExternal(external);
        ref.setDisplayName(varTxt);
        ref.setReferencedLexeme(varTxt);
        JAXBElement<Reference> jbeRef = factory.createNearReference(ref);
        Block block = factory.createBlock();
        block.setValue(": ");
        JAXBElement<Block> simple = factory.createNearSimple(block);

        Label lab = new Label();
        lab.setValue("Var");
        JAXBElement<Label> partLabel = factory.createEntryHeadingVocablePartLabel(lab);

        synp.getPhraseChildren().add(partLabel);
        synp.getPhraseChildren().add(simple);
        synp.getPhraseChildren().add(jbeRef);

        getSubSense().getSubSenseChildren().add(factory.createSubSenseClassifier(synp));
        //getSubSense().getSubSenseChildren().add(jbeRef);
    }

    private void insertPartIrreg(String piTxt) {
        piTxt = piTxt.replaceAll("<i>", "");
        piTxt = piTxt.replaceAll("</i>", "");
        piTxt = piTxt.replaceAll("\\.", "");
        piTxt = piTxt.replaceAll("Part irreg\\:", "");
        piTxt = piTxt.trim();
        //insertSynonym(piTxt);
        PhraseWithAttribs synp = factory.createPhraseWithAttribs();
        Reference.External external = factory.createReferenceExternal();
        Reference ref = factory.createReference();
        ref.setExternal(external);
        ref.setDisplayName(piTxt);
        ref.setReferencedLexeme(piTxt);
        JAXBElement<Reference> jbeRef = factory.createNearReference(ref);
        Block block = factory.createBlock();
        block.setValue(": ");
        JAXBElement<Block> simple = factory.createNearSimple(block);

        Label lab = new Label();
        lab.setValue("Part irreg");
        JAXBElement<Label> partLabel = factory.createEntryHeadingVocablePartLabel(lab);

        synp.getPhraseChildren().add(partLabel);
        synp.getPhraseChildren().add(simple);
        synp.getPhraseChildren().add(jbeRef);

        getSubSense().getSubSenseChildren().add(factory.createSubSenseClassifier(synp));
        //getSubSense().getSubSenseChildren().add(jbeRef);
    }

    private void insertSynonym(String synTxt) {
        synTxt = synTxt.replaceAll("<i>", "");
        synTxt = synTxt.replaceAll("\\.</i>", "");
        synTxt = synTxt.replaceAll("Sin\\:", "");
        synTxt = synTxt.trim();
        String[] syns = synTxt.split("\\,");
        PhraseWithAttribs synp = factory.createPhraseWithAttribs();
        for (String oneSyn : syns) {
            Reference.External external = factory.createReferenceExternal();
            Reference ref = factory.createReference();
            ref.setExternal(external);
            ref.setDisplayName(oneSyn);
            ref.setReferencedLexeme(oneSyn);
            JAXBElement<Reference> jbeRef = factory.createNearReference(ref);
            Block block = factory.createBlock();
            block.setValue(",");
            JAXBElement<Block> simple = factory.createNearSimple(block);
            synp.getPhraseChildren().add(jbeRef);
            synp.getPhraseChildren().add(simple);
        }
        synp.getPhraseChildren().remove(synp.getPhraseChildren().size() - 1);
        getSubSense().getSubSenseChildren().add(factory.createSubSenseSynonym(synp));
    }

    private void moveTranslation() {
        if (subSense != null) {
            JAXBElement<?> lastTranslation = subSense.getSubSenseChildren().remove(0);
            getSense().getPronunciationOrClassifierOrTranslation().add(lastTranslation);
            getSense().getPronunciationOrClassifierOrTranslation().remove(0);
        } else {
            throw new ParseError(-1, "Недопустимое положение символа \":\"");
        }
    }

    private void insertNumFixedExample(String exTxt) {
        exTxt = exTxt.replace("<b>", "").replace("@", "").trim();
        String[] data = exTxt.split("\\:</b>");
        String header = data[0].trim();
        String trans = data[1].trim();
        Example example = factory.createExample();
        //
        MultiFarLexer localLexer = new MultiFarLexer(new ANTLRStringStream(trans));
        Translation localtranslation = factory.createTranslation();
        Token token = localLexer.nextToken();
        Near localNear = factory.createNear();
        Far localFar = factory.createTranslationFar();
        localtranslation.getFar().add(localFar);
        localFar.getNear().add(localNear);
        String word = "";
        while (token.getType() != Token.EOF) {
            if (token.getType() == MultiFarLexer.LABEL) {
                tockenMap.addTocken(Esp2PortuTockenMap.LABEL_IN_FIXED_EXAMPLE_BODY);
                String label = token.getText().replace("<i>{", "").replace("}</i>", "");
                Label lab = new Label();
                lab.setValue(label);
                JAXBElement<Label> partLabel = factory.createEntryHeadingVocablePartLabel(lab);
                localNear.getNearChildren().add(partLabel);
            } else {
                String txt = token.getText();
                if (token.getType() == MultiFarLexer.SHARP) {
                    txt = " ";
                } else if (token.getType() == MultiFarLexer.DOUBLESHARP) {
                    txt = ", ";
                } else if (token.getType() == MultiFarLexer.PLUS) {
                    txt = "+";
                }
                word += txt;
            }
            token = localLexer.nextToken();
            if (token.getType() == MultiFarLexer.LABEL
                    || token.getType() == MultiFarLexer.EOF) {
                Block transBlock = factory.createBlock();
                transBlock.setValue(word);
                JAXBElement<Block> simpleTrans = factory.createEntryHeadingVocablePartSimple(transBlock);
                localNear.getNearChildren().add(simpleTrans);
                word = "";
            }
        }
        //
        Phrase source = factory.createPhrase();
        Block srcBlock = factory.createBlock();
        srcBlock.setValue(header);
        JAXBElement<Block> simpleSrc = factory.createEntryHeadingVocablePartSimple(srcBlock);
        source.getPhraseChildren().add(simpleSrc);
        //
        example.getTranslation().add(localtranslation);
        example.setSource(source);
        getSubSense().getSubSenseChildren().add(factory.createSubSenseFixedExample(example));

    }

    private void insertFixedExample(String exTxt, int pos) {
        getSubSense().getSubSenseChildren().addAll(new FiaxedExampleInserter(factory, tockenMap, pos).getExamples(exTxt));
    }

    private void insertExample(String exTxt) {
        exTxt = exTxt.replace("<i>", "").replaceAll("@", "").replace("</i>", "@@");
        String[] data = exTxt.split("@@");
        String header = data[0].trim();
        String trans = data[1].replaceFirst("/", "").trim();
        if (trans.endsWith(".")) {
            trans = trans.substring(0, trans.length() - 1);
        }
        if (header.contains("/")) {
            tockenMap.addTocken(Esp2PortuTockenMap.SLASH_IN_EXAMPLE_HEADER);
        }
        if (trans.contains("/")) {
            tockenMap.addTocken(Esp2PortuTockenMap.SLASH_IN_EXAMPLE_BODY);
        }
        Example example = factory.createExample();
        //
        Translation localtranslation = factory.createTranslation();
        Near localNear = factory.createNear();
        Far localFar = factory.createTranslationFar();
        localtranslation.getFar().add(localFar);
        localFar.getNear().add(localNear);
        Block transBlock = factory.createBlock();
        transBlock.setValue(trans);
        JAXBElement<Block> simpleTrans = factory.createEntryHeadingVocablePartSimple(transBlock);
        localNear.getNearChildren().add(simpleTrans);
        //
        Phrase source = factory.createPhrase();
        Block srcBlock = factory.createBlock();
        srcBlock.setValue(header);
        JAXBElement<Block> simpleSrc = factory.createEntryHeadingVocablePartSimple(srcBlock);
        source.getPhraseChildren().add(simpleSrc);
        //
        example.getTranslation().add(localtranslation);
        example.setSource(source);
        subSense.getSubSenseChildren().add(factory.createSubSenseExample(example));
    }

    private void insertReferece(String refTxt) {
        refTxt = refTxt.replace("→", "").replace(".", "").replace(" ", "");
        Reference.External external = factory.createReferenceExternal();
        Reference ref = factory.createReference();
        ref.setExternal(external);
        ref.setDisplayName(refTxt);
        ref.setReferencedLexeme(refTxt);
        JAXBElement<Reference> jbeRef = factory.createNearReference(ref);
        PhraseWithAttribs see = factory.createPhraseWithAttribs();
        see.getPhraseChildren().add(jbeRef);
        JAXBElement<PhraseWithAttribs> jbrSee = factory.createSubSenseSee(see);
        getSubSense().getSubSenseChildren().add(jbrSee);
    }

    private void insertVLabelReferece(String refTxt) {
        //subSenseClassifier = null;

        refTxt = refTxt.replace("→", "").replace(".", "").replace(" ", "");
        Reference.External external = factory.createReferenceExternal();
        Reference ref = factory.createReference();
        ref.setExternal(external);
        ref.setDisplayName(refTxt);
        ref.setReferencedLexeme(refTxt);
        JAXBElement<Reference> jbeRef = factory.createNearReference(ref);

        Block block = factory.createBlock();
        block.setValue(" ");
        JAXBElement<Block> simple = factory.createNearSimple(block);
        getSubSenseClassifier().getPhraseChildren().add(simple);
        getSubSenseClassifier().getPhraseChildren().add(jbeRef);
        //getVariantClassifier().getPhraseChildren().add(simple);
        //getVariantClassifier().getPhraseChildren().add(jbeRef);
        getNear();
    }

    private void insertComment(String commentTxt) {
        Block block = factory.createBlock();
        block.setValue(commentTxt.trim());
        JAXBElement<Block> simple = factory.createNearSimple(block);
        Near comment = factory.createNear();
        comment.getNearChildren().add(simple);
        JAXBElement<Near> jbeComment = factory.createEntryHeadingVocablePartComment(comment);
        getNear().getNearChildren().add(jbeComment);
    }

    private void insertEnglish(String word) {
        word = word.replace("^<i>", "").replace(".", "").replace("</i>", "");
        insertComment(word);
    }

    private void insertWord(String word) {
        word = word.replaceAll("@", "");
        if (warn) {
            int l = word.split(" ").length;
            if (l > 2) {
                throw new WarnException("Кол-во пробелов " + (l - 1));
            }
        }
        if (getNear().getNearChildren().size() > 0) {
            word = " " + word;
        }
        Block block = factory.createBlock();
        String ib = "<i>";
        String ie = "</i>";
        String italic = "";
        while (word.contains(ib) && word.contains(ie)) {
            int start = word.indexOf(ib);
            int stop = word.indexOf(ie);
            word = word.replaceFirst(ib, "").replaceFirst(ie, "");
            int length = stop - start - ib.length();
            italic += (length == 1 ? "" + start : "" + start + "-" + (start + length)) + ";";
        }
        if (!italic.isEmpty()) {
            tockenMap.addTocken(Esp2PortuTockenMap.ITALIC_IN_SIMPLE);
            italic = italic.substring(0, italic.length() - 1);
            block.setItalic(italic);
        }

        block.setValue(word);
        JAXBElement<Block> simple = factory.createNearSimple(block);
        getNear().getNearChildren().add(simple);
    }

    private void insertTranscription(String transcriptionStrh) {

        transcriptionStrh = transcriptionStrh.replace("[", "").replace("]", "");
        transcriptionStrh = transcriptionStrh.replaceAll("<b>", "");
        transcriptionStrh = transcriptionStrh.replaceAll("</b>", "");

        if (headingCount > 1) {
            new DoubleHeadersInserter(factory, tockenMap, this).insertSecondTranscription(headingCount, transcriptionStrh, mainHeder);

            return;
        }

        if (transcriptionStrh.contains(",")) {
            tockenMap.addTocken(Esp2PortuTockenMap.COMA_IN_TRANSCRIPTION);
        }
        String[] split = transcriptionStrh.split("\\,");
        PhraseWithAttribs pronunciation = factory.createPhraseWithAttribs();
        JAXBElement<PhraseWithAttribs> jbePronunciation = factory.createEntryContentPronunciation(pronunciation);
        if (hasAlternativeHeader) {
            getHomonym().getPronunciationOrClassifierOrTranslation().add(0, jbePronunciation);
        } else {
            getHomonym().getPronunciationOrClassifierOrTranslation().add(jbePronunciation);
        }
        for (String s : split) {
            Transcription transcription = factory.createTranscription();
            //transcription.
            transcription.setUnicodeTranscription(s);
            JAXBElement<Transcription> jbeTranscription = factory.createPhraseTranscription(transcription);
            pronunciation.getPhraseChildren().add(jbeTranscription);
            Block coma = factory.createBlock();
            coma.setValue(", ");
            pronunciation.getPhraseChildren().add(factory.createEntryHeadingVocablePartSimple(coma));
        }
        pronunciation.getPhraseChildren().remove(pronunciation.getPhraseChildren().size() - 1);
    }
    private boolean hasAlternativeHeader = false;

    private void insertAlterantiveHeader(String header) {
        hasAlternativeHeader = true;
        header = header.replace("<b style=\'color:aqua\'>", "");
        header = header.replaceAll("</b>", "");
        header = header.replaceAll("<b>", "");
        header = header.trim();
        if (header.endsWith(",")) {
            header = header.substring(0, header.length() - 1);
        }
        String[] headers = header.split(",");
        if (headers.length == 0) {
            throw new ParseError(-1, "Альтернативный заголовок пуст");
        }
        if (headers.length == 1) {
            throw new ParseError(-1, "Альтернативный заголовок должен содержать минимум 2 элемента");
        }

        String mainHeader = headers[0];
        //алгоритм извлечения номера из тега <sup>
        String numerator = "I";
        String sup = "<sup>";
        String supe = "</sup>";
        String[] nums = {"0", "I", "II", "III", "IV", "V", "VI", "VII", "VIII"};
        if (mainHeader.contains(sup)) {
            String num = header.substring(header.indexOf(sup) + sup.length());
            num = num.substring(0, header.indexOf(supe));
            num = num.replaceAll("</sup>", "");
            Integer intNum = Integer.valueOf(num);
            numerator = nums[intNum];
            mainHeader = header.replaceAll("<sup>.*</sup>", "");
        }
        //конец
        insertHeader(mainHeader);
        for (int i = 0; i < headers.length - 1; i++) {
            String alterHeader = headers[i + 1];
            alterHeader = alterHeader.replaceAll("\\.", "");
            getAlternativeEntrys().add(new AlternativeEntryCreator(factory).createAlterantiveEntry(alterHeader, mainHeader, numerator));

            Reference.External external = factory.createReferenceExternal();
            Reference ref = factory.createReference();
            ref.setExternal(external);
            ref.setDisplayName(alterHeader.trim());
            ref.setReferencedLexeme(alterHeader.trim());
            JAXBElement<Reference> jbeRef = factory.createNearReference(ref);

            PhraseWithAttribs classif = factory.createPhraseWithAttribs();
            JAXBElement<PhraseWithAttribs> lHomonymClassifier = factory.createHomonymClassifier(classif);
            Block block = factory.createBlock();
            block.setValue("= ");
            JAXBElement<Block> simple = factory.createNearSimple(block);
            classif.getPhraseChildren().add(simple);
            classif.getPhraseChildren().add(jbeRef);
            getHomonym().getPronunciationOrClassifierOrTranslation().add(lHomonymClassifier);
            //getVariantClassifier().getPhraseChildren().add(simple);
            //getVariantClassifier().getPhraseChildren().add(jbeRef);
        }
    }

    private void insertHeader(String header) {
        if (homonymCount > 1) {
            return;
        }
        headingCount++;
        //Проверка на то что второй заголовок идет сразу же за первым
        if (headingCount > 1
                && (tockenMap.contains(MultiFarLexer.PORTU_WORD)
                || tockenMap.contains(MultiFarLexer.LABEL)
                || tockenMap.contains(MultiFarLexer.NUMERATION)
                || tockenMap.contains(MultiFarLexer.SUB_NUMERATION)
                || tockenMap.contains(MultiFarLexer.EXAMPLE)
                || tockenMap.contains(MultiFarLexer.FIXED_EXAMPLE))) {
            throw new ParseError(-1, "Второй заголовок должен идти сразу же за первым");
        }

        header = header.replaceAll("<sup>.*</sup>", "");
        header = header.replace("<b style=\'color:aqua\'>", "");
        header = header.replaceAll("</b>", "");
        header = header.replaceAll("<b>", "");
        header = header.trim();
        String temp = header.replace(".", "");
        if (headingCount > 1) {
            new DoubleHeadersInserter(factory, tockenMap, this).insertSecondHeader(headingCount, temp);
            Block block1 = factory.createBlock();
            PhraseWithAttribs lclassifier = factory.createPhraseWithAttribs();
            block1.setValue("= ");
            lclassifier.getPhraseChildren().add(factory.createEntryHeadingVocablePartSimple(block1));
            JAXBElement<PhraseWithAttribs> homonymClassifier = factory.createHomonymClassifier(lclassifier);
            getHomonym().getPronunciationOrClassifierOrTranslation().add(homonymClassifier);
            Reference.External external = factory.createReferenceExternal();
            Reference ref = factory.createReference();
            ref.setExternal(external);
            ref.setDisplayName(temp);
            ref.setReferencedLexeme(temp);
            JAXBElement<Reference> jbeRef = factory.createNearReference(ref);
            lclassifier.getPhraseChildren().add(jbeRef);
            return;
        }

        ExceptionSender.headerVosklZnak(header);
        Entry.Heading entryHeading = getEntry().getHeading();
        boolean vocableIsPrim = false;
        if (entryHeading == null) {
            entryHeading = factory.createEntryHeading();
            vocableIsPrim = true;
            getEntry().setHeading(entryHeading);
        }
        Entry.Heading.Vocable entryHeadingVocable = factory.createEntryHeadingVocable();
        if (vocableIsPrim) {
            entryHeadingVocable.setIsPrimary(vocableIsPrim);
        }
        Entry.Heading.Vocable.Part entryHeadingVocablePart = factory.createEntryHeadingVocablePart();
        Block block = factory.createBlock();

        if (temp.contains(",")) {
            temp = temp.substring(0, temp.indexOf(","));
        }
        if (temp.startsWith("¡") && temp.endsWith("!")) {
            temp = temp.substring(1, temp.length() - 1);
            Block block0 = factory.createBlock();
            Block block2 = factory.createBlock();
            block0.setValue("¡");
            block.setValue(temp);
            block2.setValue("!");
            MarkType markStart = factory.createMarkType();
            markStart.setValue("");
            MarkType markEnd = factory.createMarkType();
            markEnd.setValue("");
            entryHeadingVocablePart.getPartChildren().add(factory.createEntryHeadingVocablePartMarkStart(markStart));
            entryHeadingVocablePart.getPartChildren().add(factory.createEntryHeadingVocablePartSimple(block0));
            entryHeadingVocablePart.getPartChildren().add(factory.createEntryHeadingVocablePartMarkEnd(markEnd));
            entryHeadingVocablePart.getPartChildren().add(factory.createEntryHeadingVocablePartSimple(block));
            entryHeadingVocablePart.getPartChildren().add(factory.createEntryHeadingVocablePartMarkStart(markStart));
            entryHeadingVocablePart.getPartChildren().add(factory.createEntryHeadingVocablePartSimple(block2));
            entryHeadingVocablePart.getPartChildren().add(factory.createEntryHeadingVocablePartMarkEnd(markEnd));
        } else {
            block.setValue(temp);
            entryHeadingVocablePart.getPartChildren().add(factory.createEntryHeadingVocablePartSimple(block));
        }
        mainHeder = temp;

        entryHeadingVocable.getPart().add(entryHeadingVocablePart);
        entryHeading.getVocable().add(entryHeadingVocable);
        //
        boolean needClassifire =
                header.contains(".")
                || header.contains(",")
                || header.contains("-");

        getHomonym().setNumerator("I");
        Block block1 = factory.createBlock();
        PhraseWithAttribs lclassifier = factory.createPhraseWithAttribs();
        block1.setValue(header);
        lclassifier.getPhraseChildren().add(factory.createEntryHeadingVocablePartSimple(block1));
        JAXBElement<PhraseWithAttribs> homonymClassifier = factory.createHomonymClassifier(lclassifier);
        if (needClassifire) {
            getHomonym().getPronunciationOrClassifierOrTranslation().add(homonymClassifier);
        }
    }

    public Content getContent() {
        if (content == null) {
            content = factory.createEntryContent();
            getEntry().setContent(content);
        }
        return content;
    }

    private Homonym getHomonym() {
        String[] nums = {"0", "I", "II", "III", "IV", "V", "VI", "VII", "VIII"};
        if (homonym == null) {
            homonym = factory.createHomonym();
            homonym.setNumerator(nums[homonymCount]);
            getContent().getClassifierOrPronunciationOrHomonym().add(factory.createEntryContentHomonym(homonym));
        }
        return homonym;
    }

    private void nextHomonym() {
        homonymCount++;
        homonym = null;
        variantCounter = 1;
        variant = null;
        senseCounter = 0;
        subSenseCounter = 1;
        sense = null;
        senseClassifier = null;
        subSenseClassifier = null;
        subSense = null;
        translation = null;
        far = null;
        near = null;
        getHomonym();
    }

    Variant getVariant() {
        if (variant == null) {
            variant = factory.createVariant();
            variant.setNumerator("" + variantCounter + ".");
            getHomonym().getPronunciationOrClassifierOrTranslation().add(factory.createHomonymVariant(variant));
        }
        return variant;
    }

    private void nextVariant() {
        variantCounter++;
        variant = null;
        senseCounter = 0;
        sense = null;
        senseClassifier = null;
        subSenseClassifier = null;
        subSense = null;
        subSenseCounter = 1;
        translation = null;
        far = null;
        near = null;
        getVariant();
    }

    Sense getSense() {
        if (sense == null) {
            //System.out.println("Create sense");
            sense = factory.createSense();
            if (senseCounter == 0) {
                senseCounter++;
            }
            sense.setNumerator("" + senseCounter + ")");
            getVariant().getPronunciationOrClassifierOrTranslation().add(factory.createVariantSense(sense));
        }
        return sense;
    }

//    public static void main(String[] args) {
//        String numerator = "123123123 @123";
//
//        String src = numerator.substring(0, numerator.indexOf("@")).trim();
//        System.out.println(src);
//    }
    private void nextSense(String numerator) {
        try {
            String src = numerator.substring(0, numerator.indexOf("@"));
            src = src.replace("<b>", "").trim();
            numerator = numerator.substring(numerator.indexOf("@"));
            numerator = numerator.replace("@", "").replace("<b>", "").replace("</b>", "");

            sense = null;
            senseClassifier = null;
            subSenseClassifier = null;
            subSense = null;
            translation = null;
            far = null;
            near = null;
            int next = Integer.valueOf(numerator);
            if (next - senseCounter != 1) {
                throw new ParseError(-1, "Неверный порядковый номер в sense numerator : "
                        + next + ", должно быть " + (senseCounter + 1));
            }
            senseCounter++;

            String begin = "<i>{";
            String end = "}</i>";
            while (src.startsWith(begin)) {
                //System.out.println(">> " + src);
                src = src.replaceFirst("<i>\\{", "");
                String label = src.substring(0, src.indexOf(end));
                labelInserter.insertLabel(label);
                src = src.substring(src.indexOf(end) + end.length());
                if (!src.isEmpty()) {
                    String txt = src.substring(0, src.indexOf(begin));
                    labelInserter.insertText(txt);
                    src = src.substring(txt.length());
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace(System.out);
            throw new ParseError(-1, ex.getMessage());
        }
        getSense();
    }

    private SubSense getSubSense() {
        String[] nums = {"a)", "b)", "c)", "d)", "e)", "f)", "g)", "h)", "i)", "j)", "k)"};
        String numerator = nums[subSenseCounter - 1];
        if (subSense == null) {
            subSense = factory.createSubSense();
            subSense.setNumerator(numerator);
            getSense().getPronunciationOrClassifierOrTranslation().add(factory.createSenseSubSense(subSense));
        }
        return subSense;
    }

    private void nextSubSense(String numerator) {
        //numerator = numerator.replace("@", "").replace("<b>", "").replace("</b>", "");
        subSense = null;
        subSenseClassifier = null;
        senseClassifier = null;
        translation = null;
        far = null;
        near = null;
        getSubSense();
        subSenseCounter++;
    }

    private Translation getTranslation() {
        if (translation == null) {
            translation = factory.createTranslation();
            getSubSense().getSubSenseChildren().add(factory.createSubSenseTranslation(translation));
        }
        return translation;
    }

    private Far getFar() {
        if (far == null) {
            far = factory.createTranslationFar();
            getTranslation().getFar().add(far);
        }
        return far;
    }

    private void nextFat() {
        near = null;
        far = null;
        getFar();
    }

    Near getNear() {
        if (near == null) {
            near = factory.createNear();
            getFar().getNear().add(near);
        }
        return near;
    }

    PhraseWithAttribs getVariantClassifier() {
        if (variantClassifier == null) {
            variantClassifier = factory.createPhraseWithAttribs();
            getVariant().getPronunciationOrClassifierOrTranslation().add(factory.createVariantClassifier(variantClassifier));
        }
        return variantClassifier;
    }

    PhraseWithAttribs getSenseClassifier() {
        if (senseClassifier == null) {
            senseClassifier = factory.createPhraseWithAttribs();
            getSense().getPronunciationOrClassifierOrTranslation().add(factory.createSenseClassifier(senseClassifier));
        }
        return senseClassifier;
    }

    PhraseWithAttribs getSubSenseClassifier() {
        if (subSenseClassifier == null) {
            subSenseClassifier = factory.createPhraseWithAttribs();
            getSubSense().getSubSenseChildren().add(factory.createSenseClassifier(subSenseClassifier));
        }
        return subSenseClassifier;
    }
    private int headingCount = 0;
    private Content content = null;
    private Homonym homonym = null;
    private int homonymCount = 1;
    private Variant variant = null;
    private int variantCounter = 1;
    private Sense sense = null;
    private int senseCounter = 0;
    private SubSense subSense = null;
    private int subSenseCounter = 1;
    private Translation translation = null;
    private Far far = null;
    protected Near near = null;
    private PhraseWithAttribs variantClassifier = null;
    private PhraseWithAttribs senseClassifier = null;
    private PhraseWithAttribs subSenseClassifier = null;
    private String mainHeder = "";

    @Override
    public TockenMap getTockenMap() {
        return tockenMap;
    }

    private static class ExceptionSender {

        public static void headerVosklZnak(String header) throws ParseError {
            if (header.contains("!")) {
                //throw new ParseError(-1, "Символ '!' в заголовке");
            }
            if (header.contains("¡")) {
                //throw new ParseError(-1, "Символ '¡' в заголовке");
            }
        }

        public static void synHasVar(String syn) throws ParseError {
            if (syn.contains("Var:")) {
                //throw new ParseError(-1, "Недопустимый токен 'Var:'");
            }
        }
    }
}
